package com.server.questionbank.snframe.service.OllamaService;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.server.questionbank.snframe.config.ollama.OllamaConfig;
import com.server.questionbank.snframe.domain.Ollama.Chat;
import com.server.questionbank.snframe.domain.Ollama.ChatType;
import com.server.questionbank.snframe.domain.Ollama.OllamaRespone;
import com.server.questionbank.snframe.domain.ResDTO.ResponseResult;
import com.server.questionbank.snframe.service.OllamaService.KnowledgeBaseService.KnowledgeBaseService;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
public class OllamaServiceImpl implements OllamaService {
    private static final Logger logger = LoggerFactory.getLogger(OllamaServiceImpl.class);

    @Autowired // 注入 Ollama配置
    private OllamaConfig ollamaConfig;

    @Autowired // 注入 Web客户端
    private WebClient webClient;

    @Autowired // 注入知识库服务
    private KnowledgeBaseService knowledgeBaseService;


    @Autowired // 注入redis模板类
    private StringRedisTemplate redisTemplate;

    private static final String NEW_CHAT_NAME = "";
    private static final String SESSION_USER_PREFIX = "ollama_session_user_"; // 用于存储用户的 Redis 前缀
    private static final String SESSION_CHAT_PREFIX = "ollama_session_chat_"; // 用于存储会话的 Redis 前缀
    private static final String SESSION_TIME_PREFIX = "ollama_session_time_"; // 用于存储会话时间的 Redis 前缀

    /**
     * 🌟 Spring 启动时，自动检查 Ollama 状态
     */
    @PostConstruct
    void initialize() throws Exception {
        logger.info("🔄 初始化 OllamaService...");

        // 同步阻塞检查（最多等待60秒）
        Boolean isRunning = isOllamaRunning()
                .blockOptional(Duration.ofSeconds(600))
                .orElse(false);
        if (!isRunning) {
            throw new IllegalStateException("❌ Ollama服务未运行，强制终止应用启动");
        }

        // 检查模型是否可用
        logger.info("🔄 检查 【向量模型】[{}] 模型是否可用...", ollamaConfig.getVectorModelName());
        // 同步阻塞检查（最多等待60秒）
        Boolean isVectorAvailable = isModelAvailable(ollamaConfig.getVectorModelName())
                .blockOptional(Duration.ofSeconds(60))
                .orElse(false);
        if (!isVectorAvailable)
            if (pullModel(ollamaConfig.getVectorModelName()).blockOptional().orElse(false))
                logger.info("✅ 模型[{}]已拉取成功！", ollamaConfig.getVectorModelName());

        logger.info("🔄 检查 【推理模型】[{}] 模型是否可用...", ollamaConfig.getReasoningModelName());
        // 同步阻塞检查（最多等待60秒）
        Boolean isReasoningAvailable = isModelAvailable(ollamaConfig.getReasoningModelName())
                .blockOptional(Duration.ofSeconds(60))
                .orElse(false);
        if (!isReasoningAvailable)
            if (pullModel(ollamaConfig.getReasoningModelName()).blockOptional().orElse(false))
                logger.info("✅ 模型[{}]已拉取成功！", ollamaConfig.getReasoningModelName());

        // 初始化知识库
        logger.info("🔄 初始化 知识库...");
        if (!knowledgeBaseService.initialize().blockOptional().orElse(false))
            logger.warn("⚠️ 知识库初始化失败，请检查配置！");
        logger.info("🚀 Ollama 服务器已启动！");
    }

    /**
     * 🚀 创建会话
     * @param type 会话类型
     * @param greetings 问候语句
     * @param userId 用户ID
     * @return OllamaRespone
     */
    @Override
    public OllamaRespone createChat(ChatType type, String greetings, Long userId) {
        // 生成随机的 sessionId
        String sessionId;
        do {
            sessionId = UUID.randomUUID().toString();
        } while (Boolean.TRUE.equals(redisTemplate.hasKey(SESSION_CHAT_PREFIX + sessionId)));
        logger.info("已生成 sessionId: [{}]", sessionId);


        try {
            long createTime = System.currentTimeMillis(); // 创建时间
            ObjectMapper objectMapper = new ObjectMapper();

            // 添加 （默认）会话名称 到 Redis
            ObjectNode chatTitle = objectMapper.createObjectNode();
            chatTitle.put("chatTitle", NEW_CHAT_NAME);
            chatTitle.put("time", createTime); // 时间戳
            redisTemplate.opsForList().rightPush(SESSION_CHAT_PREFIX + sessionId, objectMapper.writeValueAsString(chatTitle));

            if (greetings != null && !greetings.isEmpty())
            {
                // 添加 问候语句 到 Redis
                ObjectNode greetingsNode = objectMapper.createObjectNode();
                greetingsNode.put("role", "assistant");
                greetingsNode.put("content", greetings);
                greetingsNode.put("time", createTime); // 时间戳
                redisTemplate.opsForList().rightPush(SESSION_CHAT_PREFIX + sessionId, objectMapper.writeValueAsString(greetingsNode));
            }
        }
        catch (Exception e) {
            logger.error("创建会话时出错: {}", e.getMessage());
            return new OllamaRespone("创建会话时出错", sessionId);
        }

        if(type.getExpirationTime() > 0)
        {
            // 设置过期时间
            redisTemplate.expire(
                    SESSION_CHAT_PREFIX + sessionId,
                    type.getExpirationTime(), TimeUnit.MINUTES // 单位为分钟
            );

            // 将过期时间存储到 Redis
            redisTemplate.opsForValue().set(
                    SESSION_TIME_PREFIX + sessionId,
                    String.valueOf(type.getExpirationTime()),
                    type.getExpirationTime(), TimeUnit.MINUTES
            );
        }
        else if(type.getExpirationTime() < 0)
        {
            // 将过期时间存储到 Redis
            redisTemplate.opsForValue().set(
                    SESSION_TIME_PREFIX + sessionId,
                    String.valueOf(type.getExpirationTime())
            );

            // 将 userSessions 列表存储到 Redis
            redisTemplate.opsForList().rightPushAll(SESSION_USER_PREFIX + userId, sessionId);
        }

        logger.info("🚀 创建新会话 - Model: [{}], Session ID: [{}]", ollamaConfig.getReasoningModelName(), sessionId);

        return new OllamaRespone("会话创建成功", sessionId);
    }

    /**
     * 📝 获取会话列表
     */
    @Override
    public List<Chat> getChats(Long userId) {
        logger.info("📝 获取会话列表 - User ID: [{}]", userId);
        if (userId == null) {
            logger.error("📝 获取会话列表 - User ID 为空");
            return new ArrayList<>();
        }
        if (Boolean.FALSE.equals(redisTemplate.hasKey(SESSION_USER_PREFIX + userId))) {
            logger.info("📝 获取会话列表 - User ID [{}] 不存在，创建一个空的列表", userId);
            return new ArrayList<>();
        }

        // 从 Redis 中获取 用户的 会话 session 列表
        List<String> userSessions = redisTemplate.opsForList().range(SESSION_USER_PREFIX + userId, 0, -1);
        // 如果用户没有会话，则创建一个空的列表
        if (userSessions == null) {
            userSessions = new ArrayList<>();
        }
        // 解析聊天记录（JSON -> Map）
        ObjectMapper objectMapper = new ObjectMapper();

        return userSessions.stream()
                .map(sessionId -> {
                    Chat chat = new Chat();
                    chat.setSessionId(sessionId);

                    String chatTitle = "";
                    String newMessage = "";
                    Long time = 0L;
                    // 获取聊天记录
                    String firstMessageJson;
                    String lastMessageJson;
                    try {
                        List<String> firstMessageList = getChatHistorys(sessionId, 1, true); // 得到第一条数据，会话名称
                        List<String> lastMessageList = getChatHistorys(sessionId, 1, false); // 得到最后一条数据，最新消息
                        firstMessageJson = firstMessageList != null && !firstMessageList.isEmpty() ? firstMessageList.get(0) : null;
                        lastMessageJson = lastMessageList != null && !lastMessageList.isEmpty() ? lastMessageList.get(0) : null;
                    } catch (Exception e) {
                        logger.error("❌ 获取聊天记录失败 - Session ID: [{}]", sessionId, e);
                        return chat;
                    }

                    // 解析会话名称
                    if (firstMessageJson != null) {
                        try {
                            Map<String, String> firstMessage = objectMapper.readValue(firstMessageJson, Map.class);
                            chatTitle = firstMessage.getOrDefault("chatTitle", chatTitle);
                        } catch (IOException e) {
                            logger.error("❌ 解析第一条消息失败 - Session ID: [{}]", sessionId, e);
                        }
                    }

                    // 解析最新消息
                    if (lastMessageJson != null) {
                        try {
                            Chat lastMessage = objectMapper.readValue(lastMessageJson, Chat.class);
                            newMessage = lastMessage.getContent();
                            time = lastMessage.getTime();
                        } catch (Exception e) {
                            logger.error("❌ 解析最新消息失败 - Session ID: [{}]", sessionId, e);
                        }
                    }

                    // 填充 Chat 对象字段
                    chat.setTitle(chatTitle);
                    chat.setContent(extractPlainText(newMessage));
                    chat.setAvatar("");
                    chat.setTime(time);
                    chat.setSessionId(sessionId);

                    return chat;
                })
                .collect(Collectors.toList());
    }

    /**
     * 🚀 发送通用消息
     */
    @Override
    public Flux<OllamaRespone> sendGeneralMessage(OllamaRespone input) {
        // 获取知识库中系统提示内容
        List<String> knowledgeBaseSystemMessage = knowledgeBaseService.getSystemMessage();
        // 初始 system 消息
        String systemMessage = String.join("\n", knowledgeBaseSystemMessage);
        return sendMessage(input, systemMessage, knowledgeBaseService.getKnowledgeBases(input.getMessage()));
    }

    /**
     * 📝 获取总记录条数
     */
    @Override
    public ResponseResult<Long> getTotalRecords(String sessionId) {
        // 日志记录
        logger.info("📝 获取总记录条数 - Session ID: [{}]", sessionId);

        // 检查 Redis 中是否存在该 sessionId
        if (Boolean.FALSE.equals(redisTemplate.hasKey(SESSION_CHAT_PREFIX + sessionId))) {
            logger.warn("⚠️ 该 Session ID: [{}] 不存在聊天记录", sessionId);
            return ResponseResult.error("聊天记录不存在");
        }
        logger.info("📝 获取总记录条数 - Session ID: [{}]", sessionId);
        return ResponseResult.success(redisTemplate.opsForList().size(SESSION_CHAT_PREFIX + sessionId));
    }

    /**
     * 📝 获取聊天记录
     */
    @Override
    public ResponseResult<List<Map<String, String>>> getChatHistory(String sessionId) {
        // 日志记录
        logger.info("📝 获取聊天记录 - Session ID: [{}]", sessionId);

        // 检查 Redis 中是否存在该 sessionId
        if (Boolean.FALSE.equals(redisTemplate.hasKey(SESSION_CHAT_PREFIX + sessionId))) {
            logger.warn("⚠️ 该 Session ID: [{}] 不存在聊天记录", sessionId);
            return ResponseResult.error("聊天记录不存在");
        }

        // 获取聊天记录（返回值是 Redis 列表中的 JSON 字符串）
        List<String> chatHistoryJsonList = getChatHistorys(sessionId, -1, false);

        // 如果聊天记录为空
        if (chatHistoryJsonList == null || chatHistoryJsonList.isEmpty()) {
            logger.warn("⚠️ 该 Session ID: [{}] 没有聊天记录", sessionId);
            return ResponseResult.error("聊天记录为空");
        }


        // 解析聊天记录（JSON -> Map）
        ObjectMapper objectMapper = new ObjectMapper();
        List<Map<String, String>> chatHistory = new ArrayList<>();

        try {
            for (String messageJson : chatHistoryJsonList) {
                // 解析每一条消息
                Map<String, String> message = objectMapper.readValue(messageJson, Map.class);
                chatHistory.add(message);
            }
        } catch (IOException e) {
            logger.error("❌ 解析聊天记录失败 - Session ID: [{}]", sessionId, e);
            return ResponseResult.error("获取聊天记录失败");
        }

        String chatTitle = chatHistory.get(0).get("chatTitle");
        if (chatTitle == null || chatTitle.isEmpty() || chatTitle.equals(NEW_CHAT_NAME)) {
            try {
                if (chatHistory.size() >= 2) {
                    StringBuilder chatHistoryBuilder = new StringBuilder();
                    // 从第 2 条开始（索引 1）截取子列表
                    List<Map<String, String>> subList = chatHistory.subList(1, chatHistory.size());
                    for (Map<String, String> message : subList) {
                        if (message.get("role").equalsIgnoreCase("user"))
                            chatHistoryBuilder.append("用户1发送：").append(extractPlainText(message.get("content"))).append("，");
                        if (message.get("role").equalsIgnoreCase("assistant"))
                            chatHistoryBuilder.append("用户2回复：").append(extractPlainText(message.get("content"))).append("，");

                    }
                    chatTitle = String.join("", createchatTitle(chatHistoryBuilder.toString())
                            .collectList()  // 收集所有数据块到 List
                            .block());       // 阻塞直到流结束
                    // 更新聊天记录
                    chatHistory.get(0).put("chatTitle", extractPlainText(chatTitle));

                    // 将创建的名称写回Redis
                    ObjectNode chatTitleNode = objectMapper.createObjectNode();
                    chatTitleNode.put("chatTitle", extractPlainText(chatTitle));
                    // 检查列表长度
                    Long size = redisTemplate.opsForList().size(SESSION_CHAT_PREFIX + sessionId);
                    if (size == null || size == 0) {
                        // 如果列表为空，先插入元素
                        redisTemplate.opsForList().leftPush(SESSION_CHAT_PREFIX + sessionId, objectMapper.writeValueAsString(chatTitleNode));
                    } else {
                        // 索引0存在，直接修改
                        redisTemplate.opsForList().set(SESSION_CHAT_PREFIX + sessionId, 0, objectMapper.writeValueAsString(chatTitleNode));
                    }

                    System.out.println(chatTitle);
                }
            }
            catch (Exception e) {
                logger.error("❌ 起名失败 - Session ID: [{}]", sessionId, e);
            }
        }

        // 返回聊天记录
        logger.info("✅ 获取聊天记录成功 - Session ID: [{}]", sessionId);
        return ResponseResult.success(chatHistory);
    }

    /**
     * 🗑️ 删除会话
     * @param sessionId 会话ID
     * @param userId 用户ID
     * @return 删除结果
     */
    @Override
    public ResponseResult<Boolean> deleteChat(String sessionId, Long userId) {
        logger.info("🗑️ 删除会话 - Session ID: [{}]", sessionId);

        // 删除会话历史记录
        boolean isSessionDeleted = deleteKeyIfExists(SESSION_CHAT_PREFIX + sessionId, "会话历史记录");

        // 删除会话时间记录
        boolean isSessionTimeDeleted = deleteKeyIfExists(SESSION_TIME_PREFIX + sessionId, "会话时间记录");

        // 检查用户ID不等于-1 将用户ID对应的会话列表中删除会话ID
        if (userId != -1) redisTemplate.opsForList().remove(SESSION_USER_PREFIX + userId, 0, sessionId);

        // 如果至少有一个键被删除，返回成功
        if (isSessionDeleted || isSessionTimeDeleted) {
            logger.info("✅ 会话删除成功 - Session ID: [{}]", sessionId);
            return ResponseResult.success(true);
        }

        // 如果两个键都不存在，返回错误
        logger.warn("❌ 会话不存在 - Session ID: [{}]", sessionId);
        return ResponseResult.error("会话不存在");
    }

    /**
     * 从 Redis 聊天记录列表中获取数据（支持从后向前获取）
     * @param sessionId  会话ID
     * @param count     获取的数量（0 表示获取全部，>0 表示从后向前取 count 条）
     * @param fromStart 是否从列表的开始位置开始取数据（true 表示从后向前取，false 表示从前往后取）
     * @return 返回聊天记录的 JSON 字符串列表（按时间顺序，最早的在前）
     */
    private List<String> getChatHistorys(String sessionId, int count, boolean fromStart) {
        String key = SESSION_CHAT_PREFIX + sessionId;

        // 获取全部数据
        if (count <= 0) {
            return redisTemplate.opsForList().range(key, 0, -1);
        }

        // 检查列表长度
        long totalSize = redisTemplate.opsForList().size(key);
        if (totalSize <= 0) {
            return Collections.emptyList();
        }

        // 根据 fromStart 参数选择取数据方式
        if (fromStart) {
            // 从前往后取 count 条
            long end = Math.min(totalSize - 1, count - 1);
            return redisTemplate.opsForList().range(key, 0, end);
        } else {
            // 从后往前取 count 条（原逻辑）
            long start = Math.max(0, totalSize - count);
            return redisTemplate.opsForList().range(key, start, -1);
        }
    }

    // 提取纯文本
    private String extractPlainText(String messages) {
        try {
            StringBuilder sb = new StringBuilder();
            if (messages != null) {
                // 移除<think>标签及其内容
                String cleanedMessages = messages.replaceAll("(?s)<think>.*?</think>", "").trim();
                if (!cleanedMessages.isEmpty()) sb.append(cleanedMessages);
            }
            return sb.toString().trim();
        } catch (Exception e) {
            return messages; // 解析失败时回退
        }
    }

    // 📝 给对话取一个名称
    private Flux<String> createchatTitle(String chatHistory) {
        // 检查输入是否有效
        if (chatHistory == null || chatHistory.isBlank()) {
            return Flux.just("[ERROR] 对话历史为空");
        }

        System.out.println("chatHistory: " + chatHistory);
        String modelName = ollamaConfig.getReasoningModelName();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode requestBodyJson = objectMapper.createObjectNode();
        requestBodyJson.put("model", modelName);
        requestBodyJson.put("stream", true); // 开启流式响应

        // 构建 messages 数组
        ArrayNode messagesArray = requestBodyJson.putArray("messages");
        messagesArray.add(objectMapper.createObjectNode()
                .put("role", "system")
                .put("content", "你是一个严格的标题助手，用户会发送给你一段聊天记录，必须用4-10个汉字概括对话主题且主题中不能有标点符号，不要解释或回复对话内容。"));
        messagesArray.add(objectMapper.createObjectNode()
                .put("role", "user")
                .put("content", chatHistory));

        return webClient.post()
                .uri("/api/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.TEXT_EVENT_STREAM) // 强制流式响应
                .bodyValue(requestBodyJson)
                .retrieve()
                .bodyToFlux(JsonNode.class)
                .map(jsonNode -> {
                    JsonNode messageNode = jsonNode.path("message");
                    return messageNode.has("content") ? messageNode.path("content").asText() : "";
                })
                .onErrorResume(e -> {
                    logger.error("❌ 发送失败 - Model: [{}], 错误: [{}]", modelName, e);
                    return Flux.empty(); // 或返回错误提示
                });
    }

    /**
     * 🚀 发送消息到会话中
     * @param input 输入（要发送的数据【OllamaRespone】）
     * @param systemMessage 系统提示
     * @param knowledgeBase 知识库
     * @return Flux<OllamaRespone> 流式数据
     */
    private Flux<OllamaRespone> sendMessage(OllamaRespone input, String systemMessage, String knowledgeBase ) {
        String modelName = ollamaConfig.getReasoningModelName(); // 获取配置的模型名称
        String sessionId = input.getSessionId();
        String message = input.getMessage();
        logger.info("📝 处理请求 - Model: [{}], Session ID: [{}], Prompt: [{}]", modelName, sessionId, input);

        // 判断 sessionId 是否传入
        if (sessionId == null || sessionId.isEmpty()) {
            logger.info("⚠️ 没有传入 sessionId，一次性对话");
        }

        // 使用 ObjectMapper 创建 JSON 确保安全
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode requestBodyJson = objectMapper.createObjectNode();
        requestBodyJson.put("model", modelName);
        requestBodyJson.put("stream", true); // 开启流式响应
        // 创建一个空的 messagesArray 用于存储消息
        ArrayNode messagesArray = requestBodyJson.putArray("messages");

        // 创建一个空的 fullConversation 用于 存储完整的对话历史记录
        List<ObjectNode> fullConversation = new ArrayList<>();

        // 创建 system 消息
        messagesArray.add(objectMapper.createObjectNode()
                .put("role", "system")
                .put("content", systemMessage));

        // 检查 Redis 中是否存在该 sessionId
        if (Boolean.FALSE.equals(redisTemplate.hasKey(SESSION_CHAT_PREFIX + sessionId))) {
            logger.warn("🔑 Session ID: [{}] 不存在，正在创建新会话...", sessionId);
        }
        else
        {
            // 从 Redis 中获取历史对话
            List<String> historyMessages = redisTemplate.opsForList()
                    .range(SESSION_CHAT_PREFIX + sessionId, 1, -1);
            if (historyMessages != null) {
                historyMessages.forEach(msg -> {
                    try {
                        fullConversation.add((ObjectNode) objectMapper.readTree(msg));
                    } catch (JsonProcessingException e) {
                        logger.warn("历史消息解析失败: {}", msg);
                    }
                });
            }
        }

        // 添加新消息（将会被记录到消息记录）
        long userSendTime = System.currentTimeMillis(); // 用户发送时间
        ObjectNode userMessage = objectMapper.createObjectNode();
        userMessage.put("role", "user");
        userMessage.put("content", message);
        userMessage.put("time", userSendTime); // 时间戳

        // 拼接 知识库 内容 和 用户消息（不会被记录到消息记录）
        ObjectNode knowledgeBaseAndUserMessage = objectMapper.createObjectNode();
        knowledgeBaseAndUserMessage.put("role", "user");
        knowledgeBaseAndUserMessage.put("content", knowledgeBase + "\n以下是用户的问题：\n" + message);
        fullConversation.add(knowledgeBaseAndUserMessage);

        // 添加历史对话
        fullConversation.stream()
                .filter(msg -> msg.has("role") && msg.has("content"))
                .forEach(messagesArray::add);

//        logger.info("🚀 完整请求体：\n{}", requestBodyJson.toPrettyString());

        // 获取过期时间
        int expiration = Boolean.TRUE.equals(redisTemplate.hasKey(SESSION_TIME_PREFIX + sessionId))
                ? Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get(SESSION_TIME_PREFIX + sessionId)))
                : 0;
        if (expiration > 0) {
            logger.info("⏰ Session ID: [{}] 过期时间: [{}] 分钟", sessionId, expiration);
        } else if (expiration < 0) {
            logger.info("⏰ Session ID: [{}] 无过期时间（永久可用）", sessionId);
        } else {
            logger.info("⏰ Session ID: [{}] 为单次对话", sessionId);
        }
        // 创建一个CompletableFuture用于处理流式响应
        StringBuilder completeResponse = new StringBuilder();
        // 追加到完整响应
        //                    System.out.print(response);
        return webClient.post()
                .uri("/api/chat")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.TEXT_EVENT_STREAM) // 强制启用流式响应
                .bodyValue(requestBodyJson) // 发送 JSON 数据
                .retrieve()
                .bodyToFlux(JsonNode.class) // 处理流式数据
                .map(jsonNode -> jsonNode.path("message").path("content").asText()) // 将 JSON 转换为字符串
                .doOnNext(completeResponse::append)
                .map(responseChunk -> new OllamaRespone(responseChunk, sessionId))
                .doOnComplete(() -> {
                    // AI 回复
                    long modelResponseTime = System.currentTimeMillis();
                    ObjectNode aiMessage = objectMapper.createObjectNode();
                    aiMessage.put("role", "assistant");
                    aiMessage.put("content", completeResponse.toString());
                    aiMessage.put("time", modelResponseTime);
                    // 追加存入 Redis 列表
                    try {
                        if(expiration > 0) // 如果过期时间大于0，则设置过期时间
                        {
                            // 批量追加元素
                            redisTemplate.opsForList().rightPushAll(
                                    SESSION_CHAT_PREFIX + sessionId,
                                    objectMapper.writeValueAsString(userMessage),
                                    objectMapper.writeValueAsString(aiMessage)
                            );
                            // 设置过期时间
                            redisTemplate.expire(SESSION_CHAT_PREFIX + sessionId, expiration, TimeUnit.MINUTES);
                            // 设置过期时间
                            redisTemplate.expire(SESSION_TIME_PREFIX + sessionId, expiration, TimeUnit.MINUTES);
                        }
                        else if(expiration < 0) // 如果过期时间小于0，则不设置过期时间（永久储存）
                        {
                            redisTemplate.opsForList().rightPushAll(
                                    SESSION_CHAT_PREFIX + sessionId,
                                    objectMapper.writeValueAsString(userMessage),
                                    objectMapper.writeValueAsString(aiMessage)
                            );
                        }
                    } catch (JsonProcessingException e) {
                        logger.error("❌ 存储完整 AI 响应失败: {}, 错误信息: {}", completeResponse, e.getMessage());
                    }
                })
                .onErrorResume(e -> {
                    logger.error("❌ 发送失败 - Model: [{}], Session ID: [{}]", modelName, sessionId, e);
                    return Mono.just(new OllamaRespone("发送失败，请稍后再试", sessionId));
                });
    }

    /**
     * 🗑️ 删除指定的 Redis 键（如果存在），并记录日志。
     * @param key 要删除的键
     * @param keyDescription 键的描述
     * @return 如果键存在并删除成功，则返回 true，否则返回 false
     */
    private boolean deleteKeyIfExists(String key, String keyDescription) {
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            redisTemplate.delete(key);
            logger.info("✅ 删除 {} 成功 - Key: [{}]", keyDescription, key);
            return true;
        } else {
            logger.warn("❌ {} 不存在 - Key: [{}]", keyDescription, key);
            return false;
        }
    }

    // 🔍 检查 Ollama 服务器是否在运行
    private Mono<Boolean> isOllamaRunning() {
        logger.info("🔍 检查 Ollama 服务器是否在运行...");
        return webClient.get()
                .uri("/api/tags")
                .retrieve()
                .bodyToMono(String.class)
                .doOnSuccess(response -> logger.info("✅ Ollama 服务器在线！"))
                .map(response -> true)
                .onErrorResume(e -> {
                    logger.error("❌ Ollama 服务器未运行！");
                    return Mono.just(false);
                });
    }

    // 🔍 检查模型是否可用
    private Mono<Boolean> isModelAvailable(String modelName) {
        logger.info("🔍 检查模型 {} 是否可用...", modelName);
        return webClient.get()
                .uri("/api/tags")
                .retrieve()
                .bodyToMono(String.class)
                .map(response -> {
                    try {
                        ObjectMapper objectMapper = new ObjectMapper();
                        JsonNode jsonNode = objectMapper.readTree(response);

                        if (jsonNode.has("models") && jsonNode.get("models").isArray()) {
                            for (JsonNode model : jsonNode.get("models")) {
                                if (model.has("name") &&
                                        model.get("name").asText().equalsIgnoreCase(modelName)) {
                                    logger.info("✅ 模型 {} 已安装。", modelName);
                                    return true;
                                }
                            }
                        }
                    } catch (IOException e) {
                        logger.error("❌ 解析模型信息失败！", e);
                    }
                    logger.warn("❌ 需要的模型未安装！");
                    return false;
                })
                .onErrorResume(e -> {
                    logger.error("❌ 获取模型信息失败！", e);
                    return Mono.just(false);
                });
    }

    // 📥 下载模型
    private Mono<Boolean> pullModel(String modelName) {
        logger.info("🚀 开始拉取模型: {}", modelName);

        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode requestBody = objectMapper.createObjectNode();
        requestBody.put("name", modelName); // 要拉取的模型名称
        requestBody.put("stream", true);    // 启用流式响应

        AtomicReference<Double> stateProgress = new AtomicReference<>(0.0);
        return webClient.post()
                .uri("/api/pull") // Ollama 的模型拉取接口
                .bodyValue(requestBody) // 发送包含模型名称的 JSON
                .retrieve()
                .bodyToFlux(JsonNode.class) // 接收流式响应数据
                .doOnNext(jsonNode -> {
                    String status = jsonNode.path("status").asText();
                    long total = jsonNode.path("total").asLong(0);
                    long completed = jsonNode.path("completed").asLong(0);
                    String digest = jsonNode.path("digest").asText("");

                    // 显示进度条和下载进度
                    if (total > 0 && !digest.isEmpty()) {
                        // 解析层名称（取digest前12位）
                        double progress = (double) completed / total * 100;
                        String progressBar = buildFullWidthProgressBar(progress);
                        String formattedSize = formatBytesWithPadding(total);

                        if (progress != stateProgress.get())
                        {
                            // 输出进度
                            String logMsg = String.format(
                                    "%s... %s%% %s %s",
                                    status,
                                    String.format("%.2f", progress),
                                    progressBar,
                                    formattedSize
                            );
                            // 使用回车符实现单行刷新
                            System.out.print("\r" + logMsg);

                            // 下载完成时换行
                            if (completed == total) {
                                System.out.println();
                            }
                            stateProgress.set(progress);
                        }
                    } else {
                        logger.info("ℹ️ {}", status);
                    }
                })
                .handle((jsonNode, sink) -> { // 处理每个 JSON 数据块
                    String status = jsonNode.path("status").asText("unknown");
                    String errorMsg = jsonNode.path("error").asText("");

                    // 遇到错误状态时终止流并抛出异常
                    if ("error".equalsIgnoreCase(status)) {
                        logger.error("❌ 拉取失败 | 模型: {} | 错误信息: {}", modelName, errorMsg);
                        sink.error(new RuntimeException(errorMsg)); // 触发错误处理
                    }
                    // 正常状态传递下游
                    else {
                        sink.next(status);
                    }
                })
                .last() // 只关注最后一个状态 (Ollama 最后会发送 "success")
                .map(finalStatus -> { // 验证最终状态
                    boolean isSuccess = "success".equalsIgnoreCase(finalStatus.toString());
                    logger.info("✅ 模型拉取结果 | 模型: {} | 成功: {}", modelName, isSuccess);
                    return isSuccess;
                })
                .onErrorResume(e -> { // 错误处理 (网络错误/API错误等)
                    logger.error("❌ 模型拉取异常 | 模型: {} | 原因: {}", modelName, e.getMessage());
                    return Mono.just(false); // 返回失败状态
                });
    }

    // 构建全宽进度条（50字符宽度）
    private String buildFullWidthProgressBar(double progress) {
        int width = 50;
        int filled = (int) (progress / 100 * width);

        return "▕" + "█".repeat(filled)
                + (filled < width ? "▏" : "▕")
                + " ".repeat(Math.max(0, width - filled - 1))
                + "▏";
    }

    // 带对齐的字节格式化
    private String formatBytesWithPadding(long bytes) {
        String[] units = {"B", "KB", "MB", "GB"};
        int unitIndex = 0;
        double size = bytes;

        while (size >= 1024 && unitIndex < units.length - 1) {
            size /= 1024;
            unitIndex++;
        }

        return String.format("\u001B[33m%6.1f\u001B[0m \u001B[36m%-2s\u001B[0m",
                size, units[unitIndex]);
    }
}
